package com.deductive.service.special.special_import.service;

import com.alibaba.fastjson2.JSONObject;

import com.deductive.common.exception.ServiceException;
import it.geosolutions.geoserver.rest.GeoServerRESTManager;
import it.geosolutions.geoserver.rest.GeoServerRESTPublisher;
import it.geosolutions.geoserver.rest.HTTPUtils;
import it.geosolutions.geoserver.rest.Util;
import it.geosolutions.geoserver.rest.decoder.RESTLayer;
import it.geosolutions.geoserver.rest.decoder.RESTStyle;
import it.geosolutions.geoserver.rest.encoder.GSLayerEncoder;
import it.geosolutions.geoserver.rest.encoder.datastore.GSPostGISDatastoreEncoder;
import it.geosolutions.geoserver.rest.encoder.feature.GSFeatureTypeEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

@Service
@Slf4j
public class GeoserverPublishService {
    private List<String> successUrls = new ArrayList<>();

    public List<String> getSuccessUrls() {
        return successUrls;
    }

    @Value("${geoserver.url}")
    private String geoserverUrl;

    @Value("${geoserver.username}")
    private String geoserverUsername;

    @Value("${geoserver.password}")
    private String geoserverPassword;


    @Value("${postgresql.dbname}")
    private String dbName;

    @Value("${postgresql.username}")
    private String dbUsername;

    @Value("${postgresql.password}")
    private String dbPassword;
    @Value("${postgresql.host}")
    private String dbHost;
    @Value("${postgresql.port}")
    private int dbPort;

    @Autowired
    private AsyncTaskManager asyncTaskManager;

    @Autowired
    private TaskWorkflow taskWorkflow;


    @Async("taskExecutor")
    public String publishLayerAsync(String taskId, String workspace, String storeName, String layerName, String geometryType, String epsg) {
        String wmsUrl = null;
        asyncTaskManager.updateTaskStatus(taskId, "RUNNING", "开始发布图层");
        try {
            String result = publishLayer(workspace, storeName, layerName, geometryType, epsg);
            if (result != null) {
                asyncTaskManager.updateTaskStatus(taskId, "COMPLETED", result);
                taskWorkflow.onTaskCompleted(taskId);
                wmsUrl = result;
            } else {
                asyncTaskManager.updateTaskStatus(taskId, "FAILED", "图层发布失败");
            }
        } catch (Exception e) {
            log.error("异步发布图层时发生错误: {}", e.getMessage());
            asyncTaskManager.updateTaskStatus(taskId, "FAILED", e.getMessage());
        }
        return wmsUrl;
    }

    public GeoServerRESTManager getManager() {
        try {
            return new GeoServerRESTManager(new URL(geoserverUrl), geoserverUsername, geoserverPassword);
        } catch (Exception e) {
            log.error("连接GeoServer失败: {}", e.getMessage());
            return null;
        }
    }

    private boolean createWorkspace(GeoServerRESTManager manager, String workspace) {
        try {
            GeoServerRESTPublisher publisher = manager.getPublisher();
            if (!manager.getReader().existsWorkspace(workspace)) {
                boolean created = publisher.createWorkspace(workspace);
                if (created) {
                    log.info("工作空间 {} 创建成功", workspace);
                    return true;
                } else {
                    log.error("工作空间 {} 创建失败", workspace);
                    return false;
                }
            }
            log.info("工作空间 {} 已存在", workspace);
            return true;
        } catch (Exception e) {
            log.error("创建工作空间时发生错误: {}", e.getMessage());
            return false;
        }
    }

    private boolean createPostGISDatastore(GeoServerRESTManager manager, String workspace, String storeName) {
        try {
            if (!manager.getReader().existsDatastore(workspace, storeName)) {
                String host = dbHost;
                int port = dbPort;
                String database = dbName;
                String schema = "public";

                GSPostGISDatastoreEncoder store = new GSPostGISDatastoreEncoder(storeName);
                store.setHost(host);
                store.setPort(port);
                store.setDatabase(database);
                store.setSchema(schema);
                store.setUser(dbUsername);
                store.setPassword(dbPassword);

                boolean created = manager.getStoreManager().create(workspace, store);
                if (created) {
                    log.info("数据存储 {} 创建成功", storeName);
                    return true;
                } else {
                    log.error("数据存储 {} 创建失败", storeName);
                    return false;
                }
            }
            log.info("数据存储 {} 已存在", storeName);
            return true;
        } catch (Exception e) {
            log.error("创建数据存储时发生错误: {}", e.getMessage());
            return false;
        }
    }

    public String publishLayer(String workspace, String storeName, String layerName, String geometryType, String epsg) {
        GeoServerRESTManager manager = getManager();
        if (manager == null) {
            return null;
        }

        try {
            if (!createWorkspace(manager, workspace) || !createPostGISDatastore(manager, workspace, storeName)) {
                return null;
            }
            String encodeWorkspace = URLEncoder.encode(workspace, StandardCharsets.UTF_8.toString());
            String encodeStoreName = URLEncoder.encode(storeName, StandardCharsets.UTF_8.toString());
            String encodeLayerName = URLEncoder.encode(layerName, StandardCharsets.UTF_8.toString());
            // 创建图层样式
            String styleName = new String((layerName + "_style").getBytes(), StandardCharsets.UTF_8);
            String encodeStyleName = URLEncoder.encode(styleName, StandardCharsets.UTF_8.toString());

            String sld = createDefaultStyle(encodeLayerName, encodeStyleName, geometryType, null);
            boolean b = manager.getStyleManager().publishStyleInWorkspace(encodeWorkspace, sld, styleName);

            GeoServerRESTPublisher publisher = manager.getPublisher();
            GSFeatureTypeEncoder featureType = new GSFeatureTypeEncoder();
            featureType.setName(layerName);
            featureType.setTitle(layerName);
            featureType.setSRS("EPSG:" + epsg);

            GSLayerEncoder layerEncoder = new GSLayerEncoder();
            layerEncoder.setDefaultStyle(workspace, styleName);

            boolean published = publishDBLayer(encodeWorkspace, encodeStoreName, featureType, layerEncoder);
            if (!published) {
                // 添加额外的验证步骤
                boolean layerExists = existsLayerGroup(encodeWorkspace, encodeStoreName, encodeLayerName, true);
                if (layerExists) {
                    log.info("图层 {} 实际已发布成功，但API返回false", layerName);
                    published = true;
                }
            }

            if (!published) {
                log.error("图层 {} 发布失败", layerName);
                return null;
            }
            // 绑定样式到图层
            bindStyleToLayer(encodeWorkspace, encodeLayerName, styleName);


            log.info("图层 {} 发布成功", layerName);
            String wmsUrl = String.format("%s/%s/wms?service=WMS&version=1.1.0&request=GetMap&layers=%s:%s",
                    geoserverUrl, workspace, workspace, layerName);
            successUrls.add(wmsUrl);
            return wmsUrl;

        } catch (Exception e) {
            log.error("发布图层时发生错误: {}", e.getMessage());
            return null;
        }
    }

    public boolean existsLayerGroup(String workspace, String storeName, String name, boolean quietOnNotFound) {
        String url;
        if (workspace == null) {
            url = geoserverUrl + "/rest/layers/" + name + ".xml";
        } else {
            url = geoserverUrl + "/rest/workspaces/" + workspace + "/datastores/" + storeName + "/featuretypes/" + name + ".xml";
        }

        String composed = Util.appendQuietOnNotFound(quietOnNotFound, url);
        return HTTPUtils.exists(composed, geoserverUsername, geoserverPassword);
    }

    public boolean publishDBLayer(String workspace, String storename, GSFeatureTypeEncoder fte, GSLayerEncoder layerEncoder) {
        String ftypeXml = fte.toString();
        StringBuilder postUrl = (new StringBuilder(geoserverUrl)).append("/rest/workspaces/").append(workspace).append("/datastores/").append(storename).append("/featuretypes");
        String layername = fte.getName();
        if (layername != null && !layername.isEmpty()) {
            String configuredResult = HTTPUtils.postXml(postUrl.toString(), ftypeXml, geoserverUsername, geoserverPassword);
            boolean published = configuredResult != null;
            boolean configured = false;
            if (!published) {
                log.warn("Error in publishing (" + configuredResult + ") " + workspace + ":" + storename + "/" + layername);
            } else {
                log.info("DB layer successfully added (layer:" + layername + ")");
                if (layerEncoder == null) {
                    if (log.isErrorEnabled()) {
                        log.error("GSLayerEncoder is null: Unable to find the defaultStyle for this layer");
                    }

                    return false;
                }
//                configured = this.configureLayer(workspace, layername, layerEncoder);
//                if (!configured) {
//                    log.warn("Error in configuring (" + configuredResult + ") " + workspace + ":" + storename + "/" + layername);
//                } else {
//                    log.info("DB layer successfully configured (layer:" + layername + ")");
//                }
            }

//            return published && configured;
            return published;
        } else {
            if (log.isErrorEnabled()) {
                log.error("GSFeatureTypeEncoder has no valid name associated, try using GSFeatureTypeEncoder.setName(String)");
            }

            return false;
        }
    }


    /**
     * 删除PostGis图层
     *
     * @param workspace 工作空间名称
     * @param layerName 图层名称
     * @param styleName 样式名称
     */
    public void deletePostGisLayerAndStyle(String workspace, String layerName, String styleName) {
        GeoServerRESTManager manager = getManager();
        boolean unpublished = true;
        RESTLayer layer = manager.getReader().getLayer(workspace, layerName);
        if (null != layer) {
            unpublished = manager.getPublisher().unpublishFeatureType(workspace, dbName, layerName);
        }
        RESTStyle style = manager.getReader().getStyle(workspace, styleName);
        if (null != style) {
            deleteStyleByName(workspace, styleName);
        }
        if (!unpublished) {
            throw new ServiceException("删除图层异常，确认后重新删除");
        }
    }

    /**
     * 根据样式名称删除样式
     *
     * @param workspace 工作空间
     * @param styleName 样式名称
     */
    public void deleteStyleByName(String workspace, String styleName) {
        String publishCssStyleHttpUrl = String.format("%s/rest/workspaces/%s/styles/%s", geoserverUrl, workspace, URLEncoder.encode(styleName));
        boolean isDeleted = HTTPUtils.delete(publishCssStyleHttpUrl, geoserverUsername, geoserverPassword);
        if (!isDeleted) {
            throw new ServiceException("样式删除失败！");
        }
    }

    public boolean configureLayer(String workspace, String resourceName, GSLayerEncoder layer) throws IllegalArgumentException {
        if (workspace != null && resourceName != null && layer != null) {
            if (!workspace.isEmpty() && !resourceName.isEmpty() && !layer.isEmpty()) {
                String fqLayerName = workspace + ":" + resourceName;
                String url = geoserverUrl + "/rest/layers/" + fqLayerName + ".xml";
                String layerXml = layer.toString();
                String sendResult = HTTPUtils.postXml(url, layerXml, geoserverUsername, geoserverPassword);
                if (sendResult != null) {
                    if (log.isInfoEnabled()) {
                        log.info("Layer successfully configured: " + fqLayerName);
                    }
                } else if (log.isWarnEnabled()) {
                    log.warn("Error configuring layer " + fqLayerName + " (" + sendResult + ")");
                }

                return sendResult != null;
            } else {
                throw new IllegalArgumentException("Empty argument");
            }
        } else {
            throw new IllegalArgumentException("Null argument");
        }
    }

    private String createDefaultStyle(String layerName, String styleName, String geometryType, String iconUrl) {
        if (layerName.contains("水") ||
                layerName.contains("湖") ||
                layerName.contains("池") ||
                layerName.contains("海") ||
                layerName.contains("洋") ||
                layerName.contains("河")) {

        }
        switch (geometryType.toUpperCase()) {
            case "POINT":
            case "MULTIPOINT":
                return createPointStyleTemplate(layerName, styleName, "#FF0000", "#000000", 1, 12, iconUrl);
            case "LINESTRING":
            case "MULTILINESTRING":
                return createLineStyleTemplate(layerName, styleName, "#00ffff", 2);
            case "POLYGON":
            case "MULTIPOLYGON":
                return createPolygonStyleTemplate(layerName, styleName, "#fefe00", "#fefe00", 1);
            default:
                return createGeometryStyleTemplate();
        }
    }


    private void bindStyleToLayer(String workspace, String layerName, String styleName) throws IOException {
        String layerUrl = String.format("%s/rest/layers/%s:%s.xml", geoserverUrl, workspace, layerName);
        URL url = new URL(layerUrl);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("PUT");
        String auth = geoserverUsername + ":" + geoserverPassword;
        String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
        conn.setRequestProperty("Authorization", "Basic " + encodedAuth);
        conn.setRequestProperty("Content-Type", "application/json");
        conn.setDoOutput(true);

        JSONObject layerConfig = new JSONObject();
        JSONObject layer = new JSONObject();
        JSONObject defaultStyle = new JSONObject();
        defaultStyle.put("name", styleName);
        defaultStyle.put("workspace", workspace);
        layer.put("defaultStyle", defaultStyle);
        layerConfig.put("layer", layer);

        try (OutputStream os = conn.getOutputStream()) {
            byte[] input = layerConfig.toString().getBytes(StandardCharsets.UTF_8);
            os.write(input, 0, input.length);
        }

        int responseCode = conn.getResponseCode();
        if (responseCode < 200 || responseCode >= 300) {
            log.error("样式 {} 绑定到图层 {} 失败", styleName, layerName);
            throw new IOException("绑定样式失败");
        }
        log.info("样式 {} 成功绑定到图层 {}", styleName, layerName);
    }

    private String createPointStyleTemplate(String layerName, String styleName, String fillColor, String strokeColor, int strokeWidth, int size, String iconUrl) {
        if (iconUrl != null && !iconUrl.isEmpty()) {
            return String.format(
                    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                            "<StyledLayerDescriptor version=\"1.0.0\" " +
                            "xmlns=\"http://www.opengis.net/sld\" " +
                            "xmlns:ogc=\"http://www.opengis.net/ogc\" " +
                            "xmlns:xlink=\"http://www.w3.org/1999/xlink\" " +
                            "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
                            "xsi:schemaLocation=\"http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd\">" +
                            "<NamedLayer><Name>%s</Name><UserStyle><Name>%s</Name>" +
                            "<FeatureTypeStyle><Rule><PointSymbolizer>" +
                            "<Graphic>" +
                            "<ExternalGraphic>" +
                            "<OnlineResource xlink:type=\"simple\" xlink:href=\"%s\"/>" +
                            "<Format>image/png</Format>" +
                            "<Opacity>0.3</Opacity>" +
                            "</ExternalGraphic>" +
                            "<Size>%d</Size>" +
                            "</Graphic>" +
                            "</PointSymbolizer></Rule></FeatureTypeStyle>" +
                            "</UserStyle></NamedLayer></StyledLayerDescriptor>",
                    layerName, styleName, iconUrl, size);
        } else {
            return String.format(
                    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                            "<StyledLayerDescriptor version=\"1.0.0\" " +
                            "xmlns=\"http://www.opengis.net/sld\" " +
                            "xmlns:ogc=\"http://www.opengis.net/ogc\" " +
                            "xmlns:xlink=\"http://www.w3.org/1999/xlink\" " +
                            "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
                            "xsi:schemaLocation=\"http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd\">" +
                            "<NamedLayer><Name>%s</Name><UserStyle><Name>%s</Name>" +
                            "<FeatureTypeStyle><Rule><PointSymbolizer>" +
                            "<Graphic><Mark><WellKnownName>circle</WellKnownName>" +
                            "<Fill><CssParameter name=\"fill\">%s</CssParameter>" +
                            "<CssParameter name=\"fill-opacity\">0.3</CssParameter></Fill>" +
                            "<Stroke><CssParameter name=\"stroke\">%s</CssParameter>" +
                            "<CssParameter name=\"stroke-width\">%d</CssParameter>" +
                            "<CssParameter name=\"stroke-opacity\">0.3</CssParameter></Stroke>" +
                            "</Mark><Size>%d</Size></Graphic>" +
                            "</PointSymbolizer></Rule></FeatureTypeStyle>" +
                            "</UserStyle></NamedLayer></StyledLayerDescriptor>",
                    layerName, styleName, fillColor, strokeColor, strokeWidth, size);
        }
    }

    private String createLineStyleTemplate(String layerName, String styleName, String strokeColor, int strokeWidth) {
        return String.format(
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                        "<StyledLayerDescriptor version=\"1.0.0\" " +
                        "xmlns=\"http://www.opengis.net/sld\" " +
                        "xmlns:ogc=\"http://www.opengis.net/ogc\" " +
                        "xmlns:xlink=\"http://www.w3.org/1999/xlink\" " +
                        "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
                        "xsi:schemaLocation=\"http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd\">" +
                        "<NamedLayer><Name>%s</Name><UserStyle><Name>%s</Name>" +
                        "<FeatureTypeStyle><Rule><LineSymbolizer>" +
                        "<Stroke><CssParameter name=\"stroke\">%s</CssParameter>" +
                        "<CssParameter name=\"stroke-width\">%d</CssParameter>" +
                        "<CssParameter name=\"stroke-opacity\">0.3</CssParameter></Stroke>" +
                        "</LineSymbolizer></Rule></FeatureTypeStyle>" +
                        "</UserStyle></NamedLayer></StyledLayerDescriptor>",
                layerName, styleName, strokeColor, strokeWidth);
    }

    private String createPolygonStyleTemplate(String layerName, String styleName, String fillColor, String strokeColor, int strokeWidth) {
        return String.format(
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
                        "<StyledLayerDescriptor version=\"1.0.0\" " +
                        "xmlns=\"http://www.opengis.net/sld\" " +
                        "xmlns:ogc=\"http://www.opengis.net/ogc\" " +
                        "xmlns:xlink=\"http://www.w3.org/1999/xlink\" " +
                        "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
                        "xsi:schemaLocation=\"http://www.opengis.net/sld http://schemas.opengis.net/sld/1.0.0/StyledLayerDescriptor.xsd\">" +
                        "<NamedLayer><Name>%s</Name><UserStyle><Name>%s</Name>" +
                        "<FeatureTypeStyle><Rule><PolygonSymbolizer>" +
                        "<Fill><CssParameter name=\"fill\">%s</CssParameter>" +
                        "<CssParameter name=\"fill-opacity\">0.3</CssParameter></Fill>" +
                        "<Stroke><CssParameter name=\"stroke\">%s</CssParameter>" +
                        "<CssParameter name=\"stroke-width\">%d</CssParameter>" +
                        "<CssParameter name=\"stroke-opacity\">0.3</CssParameter></Stroke>" +
                        "</PolygonSymbolizer></Rule></FeatureTypeStyle>" +
                        "</UserStyle></NamedLayer></StyledLayerDescriptor>",
                layerName, styleName, fillColor, strokeColor, strokeWidth);
    }

    private String createGeometryStyleTemplate() {
        return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<StyledLayerDescriptor version=\"1.0.0\" \n" +
                "                       xsi:schemaLocation=\"http://www.opengis.net/sld StyledLayerDescriptor.xsd\" \n" +
                "                       xmlns=\"http://www.opengis.net/sld\" \n" +
                "                       xmlns:ogc=\"http://www.opengis.net/ogc\" \n" +
                "                       xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n" +
                "                       xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" +
                "  <NamedLayer>\n" +
                "    <Name>generic</Name>\n" +
                "    <UserStyle>\n" +
                "      <Title>Generic</Title>\n" +
                "      <Abstract>Generic style</Abstract>\n" +
                "      <FeatureTypeStyle>\n" +
                "        <Rule>\n" +
                "          <Name>raster</Name>\n" +
                "          <Title>Opaque Raster</Title>\n" +
                "          <ogc:Filter>\n" +
                "            <ogc:PropertyIsEqualTo>\n" +
                "              <ogc:Function name=\"isCoverage\"/>\n" +
                "              <ogc:Literal>true</ogc:Literal>\n" +
                "            </ogc:PropertyIsEqualTo>\n" +
                "          </ogc:Filter>\n" +
                "          <RasterSymbolizer>\n" +
                "            <Opacity>1.0</Opacity>\n" +
                "          </RasterSymbolizer>\n" +
                "        </Rule>\n" +
                "        <Rule>\n" +
                "          <Name>Polygon</Name>\n" +
                "          <Title>Grey Polygon</Title>\n" +
                "          <ogc:Filter>\n" +
                "            <ogc:PropertyIsEqualTo>\n" +
                "              <ogc:Function name=\"dimension\">\n" +
                "                <ogc:Function name=\"geometry\"/>\n" +
                "              </ogc:Function>\n" +
                "              <ogc:Literal>2</ogc:Literal>\n" +
                "            </ogc:PropertyIsEqualTo>\n" +
                "          </ogc:Filter>\n" +
                "          <PolygonSymbolizer>\n" +
                "            <Fill>\n" +
                "              <CssParameter name=\"fill\">#00b0f0</CssParameter>\n" +
                "              <CssParameter name=\"fill-opacity\">0.5</CssParameter> \n" +
                "            </Fill>\n" +
                "            <Stroke>\n" +
                "              <CssParameter name=\"stroke\">#00b0f0</CssParameter>\n" +
                "              <CssParameter name=\"stroke-width\">1</CssParameter>\n" +
                "            </Stroke>\n" +
                "          </PolygonSymbolizer>\n" +
                "        </Rule>\n" +
                "        <Rule>\n" +
                "          <Name>Line</Name>\n" +
                "          <Title>Blue Line</Title>\n" +
                "          <ogc:Filter>\n" +
                "            <ogc:PropertyIsEqualTo>\n" +
                "              <ogc:Function name=\"dimension\">\n" +
                "                <ogc:Function name=\"geometry\"/>\n" +
                "              </ogc:Function>\n" +
                "              <ogc:Literal>1</ogc:Literal>\n" +
                "            </ogc:PropertyIsEqualTo>\n" +
                "          </ogc:Filter>\n" +
                "          <LineSymbolizer>\n" +
                "            <Stroke>\n" +
                "              <CssParameter name=\"stroke\">#00b0f0</CssParameter>\n" +
                "              <CssParameter name=\"stroke-opacity\">1</CssParameter>\n" +
                "              <CssParameter name=\"fill-opacity\">0.5</CssParameter> \n" +
                "            </Stroke>\n" +
                "          </LineSymbolizer>\n" +
                "        </Rule>\n" +
                "        <Rule>\n" +
                "          <Name>point</Name>\n" +
                "          <Title>Red Square Point</Title>\n" +
                "          <ElseFilter/>\n" +
                "          <PointSymbolizer>\n" +
                "            <Graphic>\n" +
                "              <Mark>\n" +
                "                <WellKnownName>circle</WellKnownName>\n" +
                "                <Fill>\n" +
                "                  <CssParameter name=\"fill\">#040de6</CssParameter>\n" +
                "                  <CssParameter name=\"fill-opacity\">0.5</CssParameter> \n" +
                "                </Fill>\n" +
                "               \n" +
                "              </Mark>\n" +
                "              <Size>6</Size>\n" +
                "            </Graphic>\n" +
                "          </PointSymbolizer>\n" +
                "        </Rule>\n" +
                "        <VendorOption name=\"ruleEvaluation\">first</VendorOption>\n" +
                "      </FeatureTypeStyle>\n" +
                "    </UserStyle>\n" +
                "  </NamedLayer>\n" +
                "</StyledLayerDescriptor>\n";
    }

}